package util;

import edu.uci.ics.jung.graph.Graph;
import graph.GraphEdge;
import graph.GraphNode;
import graph.Network;
import io.DataReader;
import io.Database;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

public class Sampler {

	static int numPointsPerRouteBase = 80;
	static int numPointsPerRouteRange = 40;
	static int maxUser = 20;
	static int maxUsagePerUser = 20;

	/**
	 * sample facilities
	 * 
	 * @throws SQLException
	 * 
	 */
	static void sampleFacility(int num, String outFile) throws Exception {
		Database db = new Database();
		Connection conn = db.getConnection();
		BufferedWriter bw = null;
		bw = new BufferedWriter(new FileWriter(outFile));

		String tableName = "edge_coord";
		long seed1 = 92717622;
		long seed2 = 928737318;
		Random r1 = new Random(seed1);
		Random r2 = new Random(seed2);

		int baseEid = 14094601;
		int topEid = 17017660;
		int rangeEid = (topEid - baseEid);

		for (int i = 0; i < num; i++) {
			int eid = (int) (baseEid + rangeEid * r1.nextFloat());
			float ratioFromNode = r2.nextFloat();

			String sql = "select edgeid, ST_X(snode_utm), ST_Y(snode_utm), ST_X(enode_utm), ST_Y(enode_utm) from "
					+ tableName + " where edgeid = " + eid;
			Statement st = conn.createStatement();
			ResultSet rs = st.executeQuery(sql);
			if (rs == null || !rs.next()) {
				continue;
			}
			int edgeid = rs.getInt(1);
			int sx = rs.getInt(2);
			int sy = rs.getInt(3);
			int ex = rs.getInt(4);
			int ey = rs.getInt(5);

			float fx = sx + (ex - sx) * ratioFromNode;
			float fy = sy + (ey - sy) * ratioFromNode;

			// write into file
			bw.write(Sequencer.next() + "," + fx + "," + fy + "," + edgeid
					+ "," + ratioFromNode);
			bw.newLine();

			if ((i % 1000) == 0) {
				System.out.println(i);
			}
			rs.close();
			st.close();
		}
		bw.close();
		conn.close();

	}

	static void sampleF(double prob, String inFile, String outFile) {

		String sep = ";";
		int numUpper = 10;

		long seed1 = 92717622;
		long seed2 = 928737318;
		Random r1 = new Random(seed1);
		Random r2 = new Random(seed2);

		Random r3 = new Random();

		BufferedReader bufferedReader = null;
		BufferedWriter bw = null;
		try {
			bufferedReader = new BufferedReader(new FileReader(inFile));
			bw = new BufferedWriter(new FileWriter(outFile));
			// ignore first line
			bufferedReader.readLine();

			String line = null;
			int num = 0;
			while ((line = bufferedReader.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(line, sep);
				int edgeid = Integer.parseInt(st.nextToken());
				double sx = Double.parseDouble(st.nextToken());
				double sy = Double.parseDouble(st.nextToken());
				double ex = Double.parseDouble(st.nextToken());
				double ey = Double.parseDouble(st.nextToken());

				int numTrials = r3.nextInt(numUpper);
				for (int i = 0; i < numTrials; i++) {
					double val = r1.nextDouble();
					if (val <= prob) {
						double ratioFromNode = r2.nextDouble();
						double fx = sx + (ex - sx) * ratioFromNode;
						double fy = sy + (ey - sy) * ratioFromNode;
						String line2Write = Sequencer.next() + "," + fx + ","
								+ fy + "," + edgeid + "," + ratioFromNode;
						bw.write(line2Write);
						bw.newLine();
					}
				}

				num++;
				if (num % 1000 == 0) {
					System.out.println("line: " + num);
				}
			}
			bufferedReader.close();
			bw.close();

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 
	 * @param vertexFile
	 * @param edgeFile
	 * @param outFile
	 * @param numRoutes
	 * @param numEdgesPerRoute
	 * @param pointsPerEdge
	 * @param numUsage
	 * @throws Exception
	 */
	static void sampleRoutes(String vertexFile, String edgeFile,
			String outFile, int numRoutes, int numEdgesPerRoute,
			int pointsPerEdge, int numUsage) throws Exception {

		BufferedWriter bw = new BufferedWriter(new FileWriter(outFile));

		long s1 = 27363382;
		long s2 = 29817276;
		long s3 = 72621892;

		Random seidRand = new Random(s1); // start eid random and num usages
		Random posRand = new Random(s2); // pos random
		Random usageRand = new Random(s3); // usage random

		// create graph
		Network net = new Network();
		System.out.println("loading vertices...");
		HashMap<Integer, GraphNode> vertices = DataReader.loadVertices(
				vertexFile, 1024);
		System.out.println("loading edges...");
		HashMap<Integer, GraphEdge> edges = DataReader.loadEdges(vertices,
				edgeFile, 1, 1024);

		Graph<GraphNode, GraphEdge> graph = net.createUndirectedSparsegraph(
				vertices, edges);
		for (int i = 0; i < numRoutes; i++) {
			System.out.println("create " + (i + 1) + "th route");
			sampleOneRoute(graph, edges, bw, numEdgesPerRoute, pointsPerEdge,
					numUsage, seidRand, posRand, usageRand);
			System.out.println();
		}

		bw.close();
	}

	static void sampleOneRoute(Graph<GraphNode, GraphEdge> graph,
			HashMap<Integer, GraphEdge> edges, BufferedWriter bw,
			int numEdgesPerRoute, int pointsPerEdge, int numUsage,
			Random seidRand, Random posRand, Random usageRand) throws Exception {

		// start from a random edge
		int baseEid = 14094601;
		int topEid = 17017660;
		int rangeEid = (topEid - baseEid);
		int sEid = (int) (baseEid + rangeEid * seidRand.nextDouble());

		GraphEdge currEdge = edges.get(sEid);
		GraphNode src = graph.getEndpoints(currEdge).getFirst();
		GraphNode dest = graph.getEndpoints(currEdge).getSecond();

		while (currEdge == null || src == null || dest == null) {
			// resample
			sEid = (int) (baseEid + rangeEid * seidRand.nextDouble());
			currEdge = edges.get(sEid);
			src = graph.getEndpoints(currEdge).getFirst();
			dest = graph.getEndpoints(currEdge).getSecond();
		}
		// System.out.println("picking current edge done");
		List<String> lines = new ArrayList<String>();

		// print nodes
		for (int i = 0; i < numEdgesPerRoute; i++) {
			System.out.println("printing " + i + "th edge");
			double[] posArr = new double[pointsPerEdge];
			for (int j = 0; j < pointsPerEdge; j++) {
				posArr[j] = posRand.nextDouble();
			}
			Arrays.sort(posArr);
			// write nodes on this edge
			for (double ratio : posArr) {

				if (src != null && dest != null) {
					double x = src.getX() + ratio * (dest.getX() - src.getX());
					double y = src.getY() + ratio * (dest.getY() - src.getY());

					String l = x + "," + y + "," + currEdge.getId() + "," + x
							+ "," + y + "," + ratio;
					lines.add(l);
				}
			}

			GraphNode gn = graph.getEndpoints(currEdge).getSecond();

			System.out.println("reset currEdge...");
			Collection<GraphEdge> collection = graph.getIncidentEdges(gn);
			if (collection == null || collection.size() <= 1) {
				break;
			} else {
				Iterator<GraphEdge> ite = collection.iterator();
				while (ite.hasNext()) {
					GraphEdge tempEdge = ite.next();
					if (!tempEdge.equals(currEdge)) {
						currEdge = tempEdge;
						break;
					}
				}
			}
		}
		System.out.println("printing header and nodes");
		bw.write(Sequencer.next() + "," + lines.size() + "," + numUsage);
		bw.newLine();
		for (String s : lines) {
			bw.write(s);
			bw.newLine();
		}

		System.out.println("printing usages...");
		// print usages
		for (int j = 0; j < numUsage; j++) {
			int currUsage = usageRand.nextInt(maxUsagePerUser);
			bw.write(currUsage + "");
			bw.newLine();
		}
	}

	/**
	 * distanceBetweenTwoPoints as a parameter
	 * randomize numPoints
	 * 
	 */
	static void SampleRoutesFixDistance(Graph<GraphNode, GraphEdge> graph,
			HashMap<Integer, GraphEdge> edges, String vertexFile,
			String edgeFile, String outFile, int numRoutes,
			double distanceBetweenTwoPoints) throws Exception {
		BufferedWriter bw = new BufferedWriter(new FileWriter(outFile));

		long s1 = 27363382;
		long s2 = 29817276;
		long s3 = 72621892;
		long s4 = 83726215;
		long s5 = 82266534;

		Random seidRand = new Random(s1); // start eid random and num usages
		Random posRand = new Random(s2); // pos random
		Random usageRand = new Random(s3); // usage random
		Random numPointsRand = new Random(s4); // num points random gen
		Random numUsersRand = new Random(s5); // num users

		for (int i = 0; i < numRoutes; i++) {
			System.out.println("create " + (i + 1) + "th route");
			sampleOneRoute(graph, edges, bw, distanceBetweenTwoPoints,
					numPointsRand, seidRand, posRand, numUsersRand, usageRand);
			System.out.println();
		}

		bw.close();
	}

	private static void sampleOneRoute(Graph<GraphNode, GraphEdge> graph,
			HashMap<Integer, GraphEdge> edges, BufferedWriter bw,
			double distanceBetweenTwoPoints, Random numPointsRand,
			Random seidRand, Random posRand, Random numUsersRand,
			Random usageRand) throws Exception {

		// randomize the number of points
		int numPoints = numPointsPerRouteBase
				+ numPointsRand.nextInt(numPointsPerRouteRange);

		// start from a random edge
		int baseEid = 14094601;
		int topEid = 17017660;
		int rangeEid = (topEid - baseEid);
		int sEid = (int) (baseEid + rangeEid * seidRand.nextDouble());

		GraphEdge currEdge = edges.get(sEid);
		GraphNode src = graph.getEndpoints(currEdge).getFirst();
		GraphNode dest = graph.getEndpoints(currEdge).getSecond();

		// System.out.println("picking current edge done");
		List<String> lines = new ArrayList<String>();

		// first point
		double ratio = posRand.nextDouble();
		double x = src.getX() + ratio * (dest.getX() - src.getX());
		double y = src.getY() + ratio * (dest.getY() - src.getY());

		String l = x + "," + y + "," + currEdge.getId() + "," + x + "," + y
				+ "," + ratio;
		lines.add(l);

		while (lines.size() < numPoints) {
			// next point
			double distToDest = (1 - ratio) * currEdge.getWeight();
			if (distToDest > distanceBetweenTwoPoints) {
				// still same edge
				double currRatio = ratio + distanceBetweenTwoPoints
						/ currEdge.getWeight();
				double currX = currEdge.getSrcNode().getX()
						+ currRatio
						* (currEdge.getDestNode().getX() - currEdge
								.getSrcNode().getX());

				double currY = currEdge.getSrcNode().getY()
						+ currRatio
						* (currEdge.getDestNode().getY() - currEdge
								.getSrcNode().getY());

				lines.add(new String(currX + "," + currY + ","
						+ currEdge.getId() + "," + currX + "," + currY + ","
						+ currRatio));

				ratio = currRatio;
			} else {
				// next edge
				Collection<GraphEdge> incidentEdges = graph
						.getIncidentEdges(dest);
				if (incidentEdges.size() == 0)
					break;
				else if (incidentEdges.size() == 1) {
					currEdge = incidentEdges.iterator().next();
				} else {
					// random select one

					Random r = new Random();
					int index = r.nextInt(incidentEdges.size());
					Iterator<GraphEdge> ite = incidentEdges.iterator();
					for (int i = 0; i < index; i++) {
						currEdge = ite.next();
					}
				}

				double currRatio = (distanceBetweenTwoPoints - distToDest)
						/ currEdge.getWeight();
				double currX = currEdge.getSrcNode().getX()
						+ currRatio
						* (currEdge.getDestNode().getX() - currEdge
								.getSrcNode().getX());

				double currY = currEdge.getSrcNode().getY()
						+ currRatio
						* (currEdge.getDestNode().getY() - currEdge
								.getSrcNode().getY());

				lines.add(new String(currX + "," + currY + ","
						+ currEdge.getId() + "," + currX + "," + currY + ","
						+ currRatio));

				ratio = currRatio;
			}
		}

		// while (currEdge == null || src == null || dest == null) {
		// // resample
		// sEid = (int) (baseEid + rangeEid * seidRand.nextDouble());
		// currEdge = edges.get(sEid);
		// src = graph.getEndpoints(currEdge).getFirst();
		// dest = graph.getEndpoints(currEdge).getSecond();
		// }
		//
		// // print nodes
		// for (int i = 0; i < numEdgesPerRoute; i++) {
		// System.out.println("printing " + i + "th edge");
		// double[] posArr = new double[pointsPerEdge];
		// for (int j = 0; j < pointsPerEdge; j++) {
		// posArr[j] = posRand.nextDouble();
		// }
		// Arrays.sort(posArr);
		// // write nodes on this edge
		// for (double ratio : posArr) {
		//
		// if (src != null && dest != null) {
		// double x = src.getX() + ratio * (dest.getX() - src.getX());
		// double y = src.getY() + ratio * (dest.getY() - src.getY());
		//
		// String l = x + "," + y + "," + currEdge.getId() + "," + x
		// + "," + y + "," + ratio;
		// lines.add(l);
		// }
		// }
		//
		// GraphNode gn = graph.getEndpoints(currEdge).getSecond();
		//
		// System.out.println("reset currEdge...");
		// Collection<GraphEdge> collection = graph.getIncidentEdges(gn);
		// if (collection == null || collection.size() <= 1) {
		// break;
		// } else {
		// Iterator<GraphEdge> ite = collection.iterator();
		// while (ite.hasNext()) {
		// GraphEdge tempEdge = ite.next();
		// if (!tempEdge.equals(currEdge)) {
		// currEdge = tempEdge;
		// break;
		// }
		// }
		// }
		// }

		int currNumUsers = 1 + numUsersRand.nextInt(maxUser);
		System.out.println("printing header nodes...");
		bw.write(Sequencer.next() + "," + lines.size() + "," + currNumUsers);
		bw.newLine();
		for (String s : lines) {
			bw.write(s);
			bw.newLine();
		}

		System.out.println("printing usages...");

		// print usages
		for (int j = 0; j < currNumUsers; j++) {
			int currUsage = usageRand.nextInt(maxUsagePerUser);
			bw.write(currUsage + "");
			bw.newLine();
		}
	}

	public static void main(String[] args) throws Exception {

		if (args.length < 4) {
			System.out
					.println("Usage: java -jar sampler.jar <nodesFile> <edgesFile> <outFilePrefix> NumRoutes");
			System.exit(0);
		}
		long ts = System.currentTimeMillis();
		String vertexFile = args[0];
		String edgeFile = args[1];

		String outFilePrefix = args[2];
		int numRoutes = Integer.parseInt(args[3]);

		// create graph
		Network net = new Network();
		System.out.println("loading vertices...");
		HashMap<Integer, GraphNode> vertices = DataReader.loadVertices(
				vertexFile, 1024);
		System.out.println("loading edges...");
		HashMap<Integer, GraphEdge> edges = DataReader.loadEdges(vertices,
				edgeFile, 1, 1024);

		Graph<GraphNode, GraphEdge> graph = net.createUndirectedSparsegraph(
				vertices, edges);

		// int[] distances = { 50, 100, 150 }; // short, medium and long
		int[] distances = { 150 };

		for (int distance : distances) {
			String outFile = outFilePrefix + distance + ".route";
			SampleRoutesFixDistance(graph, edges, vertexFile, edgeFile,
					outFile, numRoutes, distance);
		}
		// sampleF(0.002, "D:/research/optseg/expt/edge_coord.csv",
		// "D:/cygwin/home/xiaohui/optseg/facilities0.002.csv");

		long te = System.currentTimeMillis();
		System.out.println("time(s): " + (te - ts) / 1000.0);
	}
}
